package net.tv90.bfmccore.game;

import net.tv90.bfmccore.constant.GameConstant;
import net.tv90.bfmccore.event.FlagCapturedEvent;
import net.tv90.bfmccore.event.PlayerCountryChangeEvent;
import net.tv90.bfmccore.game.view.GameModelViewHandler;
import net.tv90.bfmccore.helper.FactionHelper;
import net.tv90.bfmccore.slot.Slot;
import net.tv90.bfmccore.squad.Squad;
import net.tv90.bfmccore.util.GeneralUtil;
import net.tv90.bfmccore.util.TempPermission;
import org.bitterorange.flansmodslain.actions.Money;
import org.bitterorange.flansmodslain.actions.Point;
import org.bitterorange.flansmodslain.models.EntitySoldierWrapper;
import org.bitterorange.flansmodslain.models.PlayerSlainEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;

import java.util.*;
import java.util.stream.Collectors;

import static net.tv90.bfmccore.BFMCCore.plugin;
import static net.tv90.bfmccore.game.GameBase.firstFactionList;
import static net.tv90.bfmccore.game.GameBase.secondFactionList;
import static net.tv90.bfmccore.game.impl.GunMasterGame.currentSlotList;
import static net.tv90.bfmccore.game.impl.GunMasterGame.playerSlotProgress;
import static net.tv90.bfmccore.helper.FactionHelper.*;
import static net.tv90.bfmccore.listener.AntiCheatListener.clearInventory;
import static net.tv90.bfmccore.slot.SlotHelper.giveSlot;
import static net.tv90.bfmccore.util.Ribbon.addNumberForRibbon;

public class GameManager implements Runnable, Listener {

    public static volatile GameManager instance;
    public static Set<Player> waitingPlayers = new HashSet<>();
    //1号地图池
    public static List<GameBase> gameList_1 = new ArrayList<>();
    //2号地图池
    public static List<GameBase> gameList_2 = new ArrayList<>();
    //3号地图池
    public static List<GameBase> gameList_3 = new ArrayList<>();
    //4号地图池
    public static List<GameBase> gameList_4 = new ArrayList<>();
    //在战场内的玩家,不能按F12打开部署GUI
    public static Set<Player> inBattleFieldPlayers = new HashSet<>();
    //当前游戏
    public GameBase currentGame = null;
    //下一个游戏
    public GameBase nextGame = null;
    //当前游戏在List中的索引位置
    public int currentGameIndex = 0;
    //当前游戏地图池
    public List<GameBase> currentGamePool = null;
    //投票地图池
    public List<GameBase> voteGameList = new ArrayList<>(GameConstant.VOTE_GAME_LIST_NUM);
    //地图投票 K = 序号 V = 票数
    public Map<Integer, Integer> voteMap = new LinkedHashMap<>();
    //可以投票的玩家
    public List<Player> allowedToVotePlayers = new ArrayList<>();
    //已经投票的玩家
    public List<Player> votedPlayers = new ArrayList<>();
    //是否开始投票
    public boolean canStartVote = true;
    //当前游戏结束后的等待时间
    public int waitingTick;

    private GameManager() {
    }

    public static GameManager getInstance() {
        if (instance == null) {
            synchronized (GameManager.class) {
                if (instance == null) {
                    instance = new GameManager();
                }
            }
        }
        return instance;
    }

    public void initGame() {
        try {
            currentGame = gameList_1.get(0);
        } catch (IndexOutOfBoundsException e) {
            throw new RuntimeException("地图池中无任何地图");
        }
        nextGame = currentGame;
        currentGameIndex = 0;
        currentGamePool = gameList_1;
        this.onStart();
        (new SelectFactionThread()).runTaskTimer(plugin, 0L, 100L);
    }

    public void startVote() {
        int num = GameConstant.VOTE_GAME_LIST_NUM;
        if (currentGamePool.size() < num) {
            num = currentGamePool.size();
        }
        List<GameBase> filteredGamePool = currentGamePool.stream()
                .filter(game -> game.getGameType() != EnumGameType.START)
                .collect(Collectors.toList());
        if (filteredGamePool.size() < num) {
            num = filteredGamePool.size();
        }
        voteGameList = GeneralUtil.getRandomSublist(filteredGamePool, num);
        for (int i = 0; i < voteGameList.size(); i++) {
            voteMap.put(i, 0);
        }
        allowedToVotePlayers.addAll(Bukkit.getOnlinePlayers());
        Bukkit.getOnlinePlayers().forEach(p -> {
            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + "开始地图投票");
        });
        printVoteInfo();
        canStartVote = false;
    }

    public void printVoteInfo() {
        Bukkit.broadcastMessage("§e[管理员]投票决定下一个地图，当前地图投票情况:");
        for (int i = 0; i < voteGameList.size(); i++) {
            Bukkit.broadcastMessage("§e" + i + "." + voteGameList.get(i).getMapName_Lang() + "-" + voteGameList.get(i).gameMode + "§c :\t§b" + voteMap.get(i));
        }
        Bukkit.broadcastMessage("§e[管理员]输入指令 §b/game v <序号> §e进行地图投票");
    }

    public void voteGame(Player player, int index) {
        if (!allowedToVotePlayers.contains(player)) {
            player.sendMessage("§e[管理员]当前投票未开始或已结束");
            return;
        }
        if (index < 0 || index >= voteGameList.size()) {
            player.sendMessage("§e[管理员]输入的序号不正确");
            return;
        }
        if (votedPlayers.contains(player)) {
            player.sendMessage("§e[管理员]你已经投过票了");
            return;
        }
        GameBase game = voteGameList.get(index);
        player.sendMessage("§e[管理员]你已经投票选择了" + game.getMapName_Lang() + " " + game.gameMode);
        voteMap.put(index, voteMap.get(index) + 1);
        votedPlayers.add(player);
        printVoteInfo();
    }

    public void handleVoteResult() {
        int playerNum = Bukkit.getOnlinePlayers().size();
        if (votedPlayers.isEmpty()) {
            Bukkit.broadcastMessage("§e[管理员]无人投票，自动轮换至下一个地图");
            currentGameIndex++;
            changeMapPool(playerNum);
            nextGame = currentGamePool.get(currentGameIndex);
            return;
        }
        int maxKey = -1;
        int maxValue = -1;
        for (Map.Entry<Integer, Integer> entry : voteMap.entrySet()) {
            if (entry.getValue() > maxValue) {
                maxValue = entry.getValue();
                maxKey = entry.getKey();
            }
        }
        nextGame = voteGameList.get(maxKey);
        Bukkit.broadcastMessage("§e[管理员]投票结果为 " + nextGame.getMapName_Lang());
        currentGameIndex = voteGameList.indexOf(nextGame);
        votedPlayers.clear();
        voteMap.clear();
        allowedToVotePlayers.clear();
        voteGameList.clear();

        changeMapPool(playerNum);
    }

    private void changeMapPool(int playerNum) {
        try {
            if (playerNum <= GameConstant.SWITCH_GAME_POOL_THRESHOLD_1) {
                if (currentGamePool != gameList_1) {
                    currentGamePool = gameList_1;
                }
                if (currentGameIndex >= gameList_1.size()) {
                    currentGameIndex = 0;
                }
            } else if (playerNum <= GameConstant.SWITCH_GAME_POOL_THRESHOLD_2) {
                if (currentGamePool != gameList_2) {
                    currentGamePool = gameList_2;
                }
                if (currentGameIndex >= gameList_2.size()) {
                    currentGameIndex = 0;
                }
            } else if (playerNum <= GameConstant.SWITCH_GAME_POOL_THRESHOLD_3) {
                if (currentGamePool != gameList_3) {
                    currentGamePool = gameList_3;
                }
                if (currentGameIndex >= gameList_3.size()) {
                    currentGameIndex = 0;
                }
            } else {
                if (currentGamePool != gameList_4) {
                    currentGamePool = gameList_4;
                }
                if (currentGameIndex >= gameList_4.size()) {
                    currentGameIndex = 0;
                }
            }
        } catch (Exception e) {
            currentGameIndex = 0;
            e.printStackTrace();
        }
    }

    public void onStart() {
        canStartVote = true;
        System.out.println("当前游戏索引" + currentGameIndex + "当前游戏:" + currentGame.getGameInfo());
        handleVoteResult();
        if (currentGame != nextGame) {
            currentGame = nextGame;
        }
        System.out.println("下局游戏索引" + currentGameIndex + "下局游戏:" + currentGame.getGameInfo());
        Bukkit.getOnlinePlayers().forEach(p -> {
            p.sendMessage("§e[管理员]下局游戏地图:" + currentGame.mapName_Lang + ",游戏模式:" + currentGame.gameMode + ",交战双方:" + currentGame.firstFactionName + "-" + currentGame.secondFactionName);
            p.sendMessage("§e[管理员]下局游戏即将开始，输入 §b/game rd §e加入对局，如未能传送回主城或有其他bug，请退出重进");
        });
        currentGame.onStart(currentGame);
    }

    @EventHandler
    public void playerSlain(PlayerSlainEvent e) {

        if (currentGame.isEnd()) {
            return;
        }

        //AI击杀玩家
        if (e.isKillerAI() && !e.isKilledAI()) {
            EntitySoldierWrapper killer = e.getKillerAI();
            Player killed = e.getPlayerKilled();
            switch (currentGame.gameType) {
                case START:
                case TDM: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 1);
                    } else {
                        currentGame.subTicket(killed, 1);
                    }
                    break;
                }
                case SDM: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 10);
                        FactionHelper.getTeamer(killer).forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title 击杀" + Squad.getSquad(killed) + "小队长");
                        });
                        FactionHelper.getEnemies(killer).forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + Squad.getSquad(killed) + "小队长死亡");
                            p.playSound(p.getLocation(), "custom.Notification", 100.0F, 1.0F);
                        });
                    } else {
                        currentGame.subTicket(killed, 1);
                    }
                    break;
                }
                case GUNMASTER: {
                    currentGame.subTicket(killed, 3);
                    break;
                }
                case FRONTLINE:
                case CONQUEST: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 3);
                    } else {
                        currentGame.subTicket(killed, 2);
                    }
                    break;
                }
                case VEHICLE_STORM:
                case AIR_SUPERIORITY: {
                    currentGame.subTicket(killed, 5);
                    break;
                }
            }
        }

        //AI击杀AI
        else if (e.isKillerAI() && e.isKilledAI()) {
            EntitySoldierWrapper killer = e.getKillerAI();
            EntitySoldierWrapper killed = e.getKilledAI();
            switch (currentGame.gameType) {
                case FRONTLINE:
                case CONQUEST: {
                    currentGame.subTicket(killed, 2);
                }
                case VEHICLE_STORM:
                case AIR_SUPERIORITY: {
                    currentGame.subTicket(killed, 5);
                    break;
                }
                case GUNMASTER:
                case START:
                case TDM:
                case SDM: {
                    currentGame.subTicket(killed, 1);
                    break;
                }
            }
        }

        //玩家击杀AI
        else if (!e.isKillerAI() && e.isKilledAI()) {
            Player killer = e.getPlayerKiller();
            EntitySoldierWrapper killed = e.getKilledAI();

            if (getCountry2(killer).equals("NoFaction")) {
                return;
            }
            if (!firstFactionList.contains(killer) && !secondFactionList.contains(killer)) {
                return;
            }
            switch (currentGame.gameType) {
                case FRONTLINE:
                case CONQUEST: {
                    currentGame.subTicket(killed, 2);
                }
                case VEHICLE_STORM:
                case AIR_SUPERIORITY: {
                    currentGame.subTicket(killed, 5);
                    break;
                }
                case GUNMASTER:
                case TDM:
                case START:
                case SDM: {
                    currentGame.subTicket(killed, 1);
                    break;
                }
            }
        }

        //玩家击杀玩家
        else {
            Player killed = e.getPlayerKilled();
            Player killer = e.getPlayerKiller();

            if (getCountry2(killer).equals("NoFaction")) {
                return;
            }
            if (!firstFactionList.contains(killer) && !secondFactionList.contains(killer)) {
                return;
            }

            switch (currentGame.gameType) {
                case FRONTLINE:
                case CONQUEST: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 3);
                    } else {
                        currentGame.subTicket(killed, 2);
                    }
                    break;
                }
                case VEHICLE_STORM:
                case AIR_SUPERIORITY: {
                    currentGame.subTicket(killed, 5);
                    break;
                }
                case START:
                case TDM: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 1);
                    } else {
                        currentGame.subTicket(killed, 1);
                    }
                    break;
                }
                case SDM: {
                    if (TempPermission.hasTempPerm(killed, "bfmc.squadleader")) {
                        currentGame.subTicket(killed, 10);
                        FactionHelper.getTeamer(killer).forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title 击杀" + Squad.getSquad(killed) + "小队长");
                        });
                        FactionHelper.getEnemies(killer).forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + Squad.getSquad(killed) + "小队长死亡");
                            p.playSound(p.getLocation(), "custom.Notification", 100.0F, 1.0F);
                        });
                    } else {
                        currentGame.subTicket(killed, 1);
                    }
                    break;
                }
                case GUNMASTER: {
                    clearInventory(killer);

                    if (!killer.isOp()) {
                        try {
                            killer.setOp(true);
                            killer.performCommand("kit " + getCountry2(killer) + "ASS");
                        } finally {
                            killer.setOp(false);
                        }
                    } else {
                        killer.performCommand("kit " + getCountry2(killer) + "ASS");
                    }

                    int progress = playerSlotProgress.getOrDefault(killer, 0);
                    progress++;
                    playerSlotProgress.put(killer, progress);

                    if (progress == currentSlotList.size() - 1) {
                        firstFactionList.forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + "一名玩家已到达最后进度");
                            p.playSound(p.getLocation(), "custom.Notification", 100.0F, 1.0F);
                        });
                        secondFactionList.forEach(p -> {
                            Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + "一名玩家已到达最后进度");
                            p.playSound(p.getLocation(), "custom.Notification", 100.0F, 1.0F);
                        });
                    }

                    if (progress >= currentSlotList.size()) {
                        killed.setHealth(20);
                        Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mv tp " + killed.getName() + " world");
                        currentGame.subEnemyTicket(killer, currentGame.maxTicket);
                        addNumberForRibbon(killer, "GunMaster", "枪神", 1);
                        return;
                    }

                    Slot slot = currentSlotList.get(progress);
                    giveSlot(killer, slot);
                    currentGame.subTicket(killed, 1);
                    break;
                }
            }
        }

        if (currentGame.commanderSystem != null) {
            currentGame.commanderSystem.onPlayerSlain(e);
        }
    }

    @EventHandler
    public void playerCommand(PlayerCommandPreprocessEvent e) {
        if (currentGame.isEnd()) {
            return;
        }
        Player player = e.getPlayer();
        if (e.getMessage().equalsIgnoreCase("/suicide")) {
            switch (currentGame.gameType) {
                case START:
                case TDM: {
                    if (TempPermission.hasTempPerm(player, "bfmc.squadleader")) {
                        currentGame.subTicket(player, 1);
                    } else {
                        currentGame.subTicket(player, 1);
                    }
                    break;
                }
                case SDM: {
                    if (TempPermission.hasTempPerm(player, "bfmc.squadleader")) {
                        currentGame.subTicket(player, 10);
                    } else {
                        currentGame.subTicket(player, 1);
                    }
                    break;
                }
                case GUNMASTER: {
                    currentGame.subTicket(player, 3);
                    break;
                }
                case FRONTLINE:
                case CONQUEST: {
                    if (TempPermission.hasTempPerm(player, "bfmc.squadleader")) {
                        currentGame.subTicket(player, 3);
                    } else {
                        currentGame.subTicket(player, 2);
                    }
                    break;
                }
                case VEHICLE_STORM:
                case AIR_SUPERIORITY: {
                    currentGame.subTicket(player, 5);
                    break;
                }
            }
        }
    }

    public String getGamePoolIndex() {
        if (currentGamePool == gameList_1) {
            return "1";
        } else if (currentGamePool == gameList_2) {
            return "2";
        } else if (currentGamePool == gameList_3) {
            return "3";
        } else if (currentGamePool == gameList_4) {
            return "4";
        }
        return "0";
    }

    @EventHandler
    public void playerFactionChange(PlayerCountryChangeEvent e) {
        GameManager.inBattleFieldPlayers.remove(e.getPlayer());
        if (currentGame.isEnd()) {
            return;
        }
        if (currentGame.commanderSystem != null) {
            currentGame.commanderSystem.onPlayerCountryChange(e);
        }
        Player p = e.getPlayer();
        String oldFaction = e.getOldFaction();
    }

    @EventHandler
    public void playerQuit(PlayerQuitEvent e) {
        Player p = e.getPlayer();
        GameManager.inBattleFieldPlayers.remove(p);
        Squad.quit(p.getName(), FactionHelper.getCountry2(p));
        TempPermission.clearTempPerm(p);
        playerKit.remove(p.getName());
        playerCountry.remove(p.getName());
        if (currentGame.isEnd()) {
            return;
        }
        if (currentGame.commanderSystem != null) {
            currentGame.commanderSystem.onPlayerQuit(e);
        }
        waitingPlayers.remove(p);
        GameBase.inGamePlayerList.remove(p);
        GameBase.firstFactionList.remove(p);
        GameBase.secondFactionList.remove(p);
        currentGame.updateSize();
        playerSlotProgress.remove(p);
    }

    @EventHandler
    public void playerDeath(PlayerDeathEvent e) {
        Player p = e.getEntity();
        GameManager.inBattleFieldPlayers.remove(p);
    }

    @EventHandler
    public void playerJoin(PlayerJoinEvent e) {
        if (currentGame.isEnd()) {
            return;
        }
        currentGame.updateSize();
    }

    @EventHandler
    public void playerConquest(FlagCapturedEvent e) {
        Player p = e.getPlayer();
        p.sendMessage("§e[征服夺旗]你获得20军衔积分和40游戏币的奖励");
        Point.add(p, 20);
        Money.give(p, 40);
        if (p.hasPermission("bfmc.vip2")) {
            p.sendMessage("§e[VIP战斗增益]你额外获得5军衔和10游戏币");
            Point.add(p, 5);
            Money.give(p, 10);
        }
        addNumberForRibbon(p, "Flag", "征服夺旗", 4);
    }

    @Override
    public void run() {
        if (currentGame.isAboutToEnd() && canStartVote) {
            startVote();
        }
        if (currentGame.isEnd()) {
            waitingPlayers.clear();
            waitingTick++;
            if (waitingTick == (GameConstant.TICK_RATE * 10) || waitingTick == (GameConstant.TICK_RATE * 15)) {
                Bukkit.getOnlinePlayers().forEach(p -> {
                    Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "title " + p.getName() + " title " + "开始地图投票");
                });
                printVoteInfo();
            }
            if (waitingTick % (GameConstant.TICK_RATE * 5) == 0) {
                int time = (GameConstant.MAX_WAITING_TICK - waitingTick) / GameConstant.TICK_RATE;
                Bukkit.broadcastMessage("§e[管理员]下一轮对局将在 " + time + "s 后开始");
            }
        }
        if (waitingTick >= GameConstant.MAX_WAITING_TICK) {
            this.onStart();
            waitingTick = 0;
        }
    }

    public class SelectFactionThread extends BukkitRunnable {
        @Override
        public void run() {
            waitingPlayers.forEach((p) -> GameModelViewHandler.getInstance().createSelectFactionGUI(p, currentGame.firstFactionName, currentGame.secondFactionName));
        }
    }
}
